/*
 * Copyright (c) 2011, Yawning <yawninglol at gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   * Neither the name of the Mew Developers nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

package yawning.mew.protwarrior;

import yawning.mew.sim.SimulationEngine;
import yawning.mew.sim.SimulationEvent;
import yawning.mew.protwarrior.ProtWarriorSimulation.SimulatorProperty;

public class VengeanceEvent extends SimulationEvent {
	ProtWarriorSimulator mState;
	private boolean mIsEnabled;

	@SuppressWarnings("unused")
	private VengeanceSamplerEvent mSamplerEvent;
	private class VengeanceSamplerEvent extends SimulationEvent {
		private int mIdx;

		protected VengeanceSamplerEvent(SimulationEngine su) {
			super(su);

			mVengeanceSamples = new double[(int) Math.ceil(su.getEndTime())];
			schedule(su.getEndTime(), 1.0f);
		}

		@Override
		public void onTick() {
			mVengeanceSamples[mIdx++] = Math.round(mCurrentVengeance);
		}
	}

	VengeanceEvent(ProtWarriorSimulator state) {
		super(state.mSimEngine);
		mState = state;

		mIsEnabled = mState.mSimEngine.getConfig().getBool(SimulatorProperty.VENGANCE);
		mSamplerEvent = new VengeanceSamplerEvent(state.mSimEngine);
	}

	@Override
	public void onTick() {
		// Basically, the first time you receive Vengeance, it will tick every 2 seconds.
		// If within the last 2 seconds you were never attacked at all, then Vengeance decays by 10% of the highest value of Vengeance achieved.
		// If you were attacked (whether successfully or not), Vengeance decays by 5% of current Vengeance, and adds 5% of total damage received during the 2 seconds.
		double newVengeance;
		double prevVengeance = mCurrentVengeance;

		if (mLastAttackedAt < mState.mSimEngine.getSimTime() - 2.0) {
			// Was not attacked at all during the last 2 sec.
			newVengeance = Math.max(mCurrentVengeance - mMaxVengeance * 0.1, 0);
		} else {
			if (mState.mModel.bug_vengeanceDecay)	// Dumb shit they shipped no. 3 million.  Vengeance decay doesn't.
				newVengeance = mCurrentVengeance;
			else
				newVengeance = Math.max(mCurrentVengeance - mCurrentVengeance * 0.05, 0);
			newVengeance = Math.max(newVengeance + mWindowDamageTaken * 0.05, mWindowDamageTaken * 0.33);
		}

		// Enforce the Vengeance cap.
		newVengeance = Math.min(newVengeance, mState.mModel.getVengeanceCap());
		mMaxVengeance = Math.max(mMaxVengeance, newVengeance);	// XXX: How does max vengeance seen behave wrt Frenzied regen?

		// Apply the new amount.
		mState.mModel.addAttackPower(-mCurrentVengeance);	// Wipe the old value.
		mState.mModel.addAttackPower(newVengeance);
		mCurrentVengeance = newVengeance;

		// Logging.
		if (mState.mIsLogging && mCurrentVengeance != prevVengeance) mState.mLog.log("Vengeance: %d / %d\n", Math.round(mCurrentVengeance), Math.round(mState.mModel.getVengeanceCap()));

		// Wipe the damage tracker.
		mWindowDamageTaken = 0;
	}

	public void onAttacked(int damage) {
		if (mIsEnabled) {
			// The Vengeance timer ticks at 2 sec intervals.
			if (!isUp()) schedule(Double.MAX_VALUE, 2.0f);

			mLastAttackedAt = mState.mSimEngine.getSimTime();
			mWindowDamageTaken += damage;
		}
	}

	public double getAverageVengeance() {
		long acc = 0;

		for (int i = 0; i < mVengeanceSamples.length; i++) {
			acc += mVengeanceSamples[i];
		}

		return (mVengeanceSamples != null) ? (double) acc / mVengeanceSamples.length : 0;
	}

	private double mCurrentVengeance;
	private double mMaxVengeance;
	private int mWindowDamageTaken;
	private double mLastAttackedAt;
	double mVengeanceSamples[];
}